package helper

import (
	"bytes"
	"encoding/json"
	"errors"
	"io"
	"mime/multipart"
	"net/http"
	"net/url"
	"os"
	"strings"
	"time"
    
    "collecter/app/lang"
)

var client *http.Client = &http.Client{Timeout: 60 * time.Second}

/**
 * Request
 * @Description: http 客户端封装：发起 http 请求
 * @param url 请求路径
 * @param method 请求方式，目前支持：get、head、options、post、put、delete
 * @param params 请求参数。get 请求的参数可以放在这里也可以直接拼接 url 里面
 * @param headers 设置请求头
 * @return error
 * @return *http.Response
 */
func Request(url string, method string, params map[string]string, headers map[string]string) (error, *http.Response) {
	if len(url) == 0 || len(method) == 0 || !RegExpVerify(url, "url") {
		return errors.New(lang.CodeMessage(6)), nil
	}

	mfMaps := map[string]func(string, map[string]string, map[string]string) (error, *http.Response){
		"get":     getRequest,
		"head":    headRequest,
		"options": getRequest,
		"post":    postRequest,
		"put":     postRequest,
		"delete":  postRequest,
	}
	method = strings.ToLower(method)
	if fn, ok := mfMaps[method]; !ok {
		return errors.New(lang.CodeMessage(10003)), nil
	} else {
		return fn(url, params, headers)
	}
}

/**
 * UpFile
 * @Description: 以 go 作为客户端上传文件到远端服务器
 * @param url 上传地址
 * @param filepath 要上传的文件在本地的保存路径
 * @param fileIndex 表单保存文件内容的 name 字段名
 * @return error
 * @return *http.Response
 */
func UploadFile(requrl string, filepath string, fileIndex string) (error, *http.Response) {
	if len(requrl) == 0 || len(filepath) == 0 {
		return errors.New(lang.CodeMessage(6)), nil
	}
	if fileinfo, err := os.Stat(filepath); err != nil {
		if os.IsNotExist(err) {
			return errors.New(lang.CodeMessage(10004)), nil
		}
		return err, nil
	} else {
		if fileinfo.IsDir() {
			return errors.New(lang.CodeMessage(10005)), nil
		}
	}

	file, err := os.OpenFile(filepath, os.O_RDONLY, 0755)
	defer func() {
		_ = file.Close()
	}()
	if err != nil {
		return err, nil
	}
	body := &bytes.Buffer{}
	//创建一个multipart类型的写文件
	writer := multipart.NewWriter(body)
	defer func() {
		_ = writer.Close()
	}()

	if part, err := writer.CreateFormFile(fileIndex, filepath); err != nil {
		return err, nil
	} else {
		if _, err := io.Copy(part, file); err != nil {
			return err, nil
		}
	}

	request, err := http.NewRequest("POST", requrl, body)
	//writer.FormDataContentType() ： 返回w对应的HTTP multipart请求的Content-Type的值，多以multipart/form-data起始
	request.Header.Set("Content-Type", writer.FormDataContentType())
	//设置host，只能用request.Host = “”，不能用request.Header.Add(),也不能用request.Header.Set()来添加host
	if u, err := url.Parse(requrl); err != nil {
		return err, nil
	} else {
		request.Host = u.Host
	}
	t := http.DefaultTransport.(*http.Transport).Clone()
	t.MaxIdleConns = 100
	t.MaxConnsPerHost = 100
	t.MaxIdleConnsPerHost = 100
	client.Transport = t
	defer client.CloseIdleConnections()

	var resp *http.Response

	if res, err := client.Do(request); err != nil {
		return err, nil
	} else {
		resp = res
	}
	defer func() {
		_ = resp.Body.Close()
	}()
	return nil, resp
}


func headRequest(url string, params map[string]string, headers map[string]string) (error, *http.Response) {
	if len(params) > 0 {
		if !strings.Contains(url, "?") {
			url += "?"
		}
		for key, val := range params {
			url += key + "=" + val + "&"
		}
		url = strings.TrimRight(url, "&")
	}
	var req *http.Request
	if r, err := http.NewRequest("HEAD", url, nil); err != nil {
		return err, nil
	} else {
		req = r
	}
	if len(headers) > 0 {
		for key, val := range headers {
			req.Header.Set(key, val)
		}
	}
	
	if res, err := client.Do(req); err != nil {
		return err, nil
	} else {
		return nil, res
	}
}

func getRequest(url string, params map[string]string, headers map[string]string) (error, *http.Response) {
	if len(params) > 0 {
		if !strings.Contains(url, "?") {
			url += "?"
		}
		for key, val := range params {
			url += key + "=" + val + "&"
		}
		url = strings.TrimRight(url, "&")
	}

	var req *http.Request
	if r, err := http.NewRequest("GET", url, nil); err != nil {
		return err, nil
	} else {
		req = r
	}
	defer func() {
		_ = req.Body.Close()
	}()
	if len(headers) > 0 {
		for key, val := range headers {
			req.Header.Set(key, val)
		}
	}

	if res, err := client.Do(req); err != nil {
		_ = req.Body.Close()
		return err, nil
	} else {
		_ = req.Body.Close()
		return nil, res
	}

	/*if data, err := io.ReadAll(resp.Body); err != nil {
		return err, ""
	} else {
		return nil, string(data)
	}*/
}

func postRequest(url string, params map[string]string, headers map[string]string) (error, *http.Response) {
	var reqData string
	if len(params) > 0 {
		if val, err := json.Marshal(params); err != nil {
			return err, nil
		} else {
			reqData = string(val)
		}
	}

	var req *http.Request
	if r, err := http.NewRequest("POST", url, strings.NewReader(reqData)); err != nil {
		return err, nil
	} else {
		req = r
	}
	defer func() {
		_ = req.Body.Close()
	}()

	if len(headers) > 0 {
		for key, val := range headers {
			req.Header.Set(key, val)
		}
	}

	if res, err := client.Do(req); err != nil {
		_ = req.Body.Close()
		return err, nil
	} else {
		_ = req.Body.Close()
		return nil, res
	}

	/*if data, err := io.ReadAll(resp.Body); err != nil {
		return err, nil
	} else {
		return nil, string(data)
	}*/
}
